#ifndef MATERIAL_H
#define MATERIAL_H

#include "Ray.h"
#include "texture.h"
#include "hittable.h"

class Texture;
class HitRecord;

class Material
{
public:
    virtual ~Material() = default;

    virtual bool scatter(
        const Ray& r_in, const HitRecord& rec, Color& attenuation, Ray& scattered,double&pdf)const;

    virtual bool scatter(const Ray& r_in, const HitRecord& rec, ScatterRecord& srec) const;

    //virtual color emitted(double u, double v, const point3& p) const;
    virtual Color emitted(const Ray& r_in, const HitRecord& rec, double u, double v, const Point3& p) const;

    // enable importance sampling
    virtual double scattering_pdf(const Ray& r_in, const HitRecord& rec, const Ray& scattered)const;
};


class Lambertian : public Material
{
public:
    Lambertian(const Color& albedo);

    Lambertian(std::shared_ptr<Texture> albedo);

    bool scatter(const Ray& r_in, const HitRecord& rec, Color& attenuation, Ray& scattered, double& pdf)const override;

    bool scatter(const Ray& r_in, const HitRecord& rec, ScatterRecord& srec) const override;

    virtual double scattering_pdf(const Ray& r_in, const HitRecord& rec, const Ray& scattered) const override;

private:
    std::shared_ptr<Texture> tex;
};

class Metal : public Material
{
public:
    Metal(const Color& albedo,double fuzz);

    bool scatter(const Ray& r_in, const HitRecord& rec, Color& attenuation, Ray& scattered, double& pdf)
        const override;
    bool scatter(const Ray& r_in, const HitRecord& rec, ScatterRecord& srec) const override;

private:
    Color albedo;
    double fuzz;
};

class Dielectric : public Material
{
public:
    Dielectric(double refraction_index);

    bool scatter(const Ray& r_in, const HitRecord& rec, Color& attenuation, Ray& scattered, double& pdf)
        const override;

    bool scatter(const Ray& r_in, const HitRecord& rec, ScatterRecord& srec) const override;

private:
    // Refractive index in vacuum or air, or the ratio of the material's refractive index over
    // the refractive index of the enclosing media
    double refraction_index;

    static double reflectance(double cosine, double refraction_index);
};


class DiffuseLight : public Material {
public:
    DiffuseLight(std::shared_ptr<Texture> tex);
    DiffuseLight(const Color& emit);

    Color emitted(const Ray& r_in, const HitRecord& rec, double u, double v, const Point3& p) const override;

private:
    std::shared_ptr<Texture> tex;
};


class Isotropic : public Material {
public:
    Isotropic(const Color& albedo);

    Isotropic(shared_ptr<Texture> tex);

    bool scatter(const Ray& r_in, const HitRecord& rec, Color& attenuation, Ray& scattered, double& pdf)const override;

    bool scatter(const Ray& r_in, const HitRecord& rec,  ScatterRecord& srec) const override;

    virtual double scattering_pdf(const Ray& r_in, const HitRecord& rec, const Ray& scattered) const override;

private:
    shared_ptr<Texture> tex;
};

#endif